package cn.tpshion.config.service;

import cn.tpshion.config.support.ConfigException;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.CuratorCache;
import org.apache.curator.framework.recipes.cache.CuratorCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Objects;
import java.util.Properties;

public class ZookeeperService {

    private static final Logger log = LoggerFactory.getLogger(ZookeeperService.class);

    /**
     * zookeeper地址
     */
    private String host;

    /**
     * 配置域
     */
    private String name;

    private String path;

    private static CuratorFramework zkClient;

    private Properties properties;

    /**
     * 初始化连接
     */
    public void init(){
        try {
            // 因为zookeeper以节点的方式获取数据，因此需要加上“/”
            path = "/" + name;
            RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
            zkClient = CuratorFrameworkFactory.builder()
                    .connectString(this.host)
                    .sessionTimeoutMs(60 * 1000)
                    .connectionTimeoutMs(15 * 1000)
                    .retryPolicy(retryPolicy)
                    .namespace("config")
                    .build();
            zkClient.start();
            log.info("配置中心初始化完成");
            // 判断配置是否存在
            Stat stat = zkClient.checkExists().forPath(path);
            if(Objects.isNull(stat)){
                log.error("{} 配置不存在，请先在配置中心完成该配置内容！", this.name);
                throw new ConfigException(this.name + " 配置不存在，请先在配置中心完成该配置内容！");
            }else{
                addConfig();
            }
            configListener();
        }catch (Exception e){
            log.error("配置拉取失败：",e.getMessage(),e);
            throw new ConfigException("配置拉取失败");
        }
    }

    /**
     * 关闭连接
     */
    public void close(){
        if(Objects.nonNull(zkClient)){
            zkClient.close();
        }
    }

    /**
     * 添加远程配置
     */
    private void addConfig() throws Exception{
        // 获取zookeeper的配置
        List<String> list = zkClient.getChildren().forPath(path);
        for (String key : list) {
            String data = new String(zkClient.getData().forPath(path + "/" + key));
            log.debug("拉取配置中心配置: 配置域={} key={} value={}",path,key, data);
            properties.put(key,data);
        }
    }

    /**
     * 监听远程配置
     */
    private void configListener(){
        CuratorCache curatorCache = CuratorCache.build(zkClient,path);
        CuratorCacheListener curatorCacheListener = new CuratorCacheListener() {
            @Override
            public void event(Type type, ChildData oldData, ChildData data) {
                if(Type.NODE_CREATED.equals(type)){
                    String childPath = data.getPath();
                    if(path.equalsIgnoreCase(childPath)){
                        return;
                    }
                    String key = childPath.substring(path.length() + 1);
                    String value = new String(data.getData());
                    log.info("配置中心新增配置: 配置域={} 节点={} key={} value={}",name,childPath,key,value);
                    properties.put(key, value);
                }
                if(Type.NODE_CHANGED.equals(type)){
                    String childPath = data.getPath();
                    if(path.equalsIgnoreCase(childPath)){
                        return;
                    }
                    String key = path.equalsIgnoreCase(childPath) ? childPath.substring(1) : childPath.substring(path.length() + 1);
                    String value = new String(data.getData());
                    log.info("配置中心修改配置: 配置域={} 节点={} key={} value={}",name,childPath,key,value);
                    properties.put(key, value);
                }
                if(Type.NODE_DELETED.equals(type)){
                    String childPath = oldData.getPath();
                    if(path.equalsIgnoreCase(childPath)){
                        log.warn("注意！配置中心删除域配置：{} 请关注应用配置状况。",childPath);
                        return;
                    }
                    String key = path.equalsIgnoreCase(childPath) ? childPath.substring(1) : childPath.substring(path.length() + 1);
                    String value = new String(oldData.getData());
                    log.info("配置中心删除配置: 配置域={} 节点={} key={} value={}",name,childPath,key,value);
                    properties.remove(key);
                }
            }
        };

        curatorCache.listenable().addListener(curatorCacheListener);
        curatorCache.start();
    }

    public ZookeeperService(String host, String name, Properties properties) {
        this.host = host;
        this.name = name;
        this.properties = properties;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public static CuratorFramework getZkClient() {
        return zkClient;
    }

    public static void setZkClient(CuratorFramework zkClient) {
        ZookeeperService.zkClient = zkClient;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }
}
